
package net.qiqbframework.messaging.annotation;


import net.qiqbframework.common.sorting.Priority;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.MetaData;
import net.qiqbframework.common.utils.AnnotationUtil;

import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Optional;


@Priority(Priority.LOW)
public class DefaultParameterResolverFactory implements HandlerParameterResolverFactory {

    @Override
    public HandlerParameterResolver createInstance(Executable executable, Parameter[] parameters, int parameterIndex) {

        Class<?> parameterType = parameters[parameterIndex].getType();
        if (Message.class.isAssignableFrom(parameterType)) {
            return new MessageParameterResolver(parameterType);
        }
        Optional<Map<String, Object>> metaDataValueAnnotation = AnnotationUtil.findAnnotationAttributes(parameters[parameterIndex], MetaDataValue.class);
        if (metaDataValueAnnotation.isPresent()) {
            return new AnnotatedMetaDataParameterResolver(metaDataValueAnnotation.get(), parameterType);
        }
        if (MetaData.class.isAssignableFrom(parameterType)) {
            return MetaDataParameterResolver.INSTANCE;
        }
        if (parameterIndex == 0) {
            Class<?> payloadType = (Class<?>) AnnotationUtil.findAnnotationAttributes(executable, MessageHandler.class)
                    .map(attr -> attr.get("payloadType"))
                    .orElse(Object.class);
            //
            if (payloadType.isAssignableFrom(parameterType)) {

                return new PayloadParameterResolver(parameterType);
            }
        }
        return null;
    }

    private static class AnnotatedMetaDataParameterResolver implements HandlerParameterResolver<Object> {

        private static final String REQUIRED_PROPERTY = "required";
        private static final String META_DATA_VALUE_PROPERTY = "metaDataValue";

        private final Map<String, Object> metaDataValue;
        private final Class parameterType;

        public AnnotatedMetaDataParameterResolver(Map<String, Object> metaDataValue, Class parameterType) {
            this.metaDataValue = metaDataValue;
            this.parameterType = parameterType;
        }

        @Override
        public Object resolveParameter(Message<?> message) {
            return message.getMetaData().get(metaDataValue.get(META_DATA_VALUE_PROPERTY).toString());
        }

        @Override
        public boolean support(Message<?> message) {
            return !(parameterType.isPrimitive() || (boolean) metaDataValue.get(REQUIRED_PROPERTY))
                    || (
                    message.getMetaData().containsKey(metaDataValue.get(META_DATA_VALUE_PROPERTY).toString())
                            && parameterType.isInstance(message.getMetaData().get(metaDataValue.get(META_DATA_VALUE_PROPERTY).toString()))
            );
        }
    }

    private static final class MetaDataParameterResolver implements HandlerParameterResolver {

        private static final MetaDataParameterResolver INSTANCE = new MetaDataParameterResolver();

        private MetaDataParameterResolver() {
        }

        @Override
        public Object resolveParameter(Message message) {
            return message.getMetaData();
        }

        @Override
        public boolean support(Message message) {
            return true;
        }
    }

    private static class MessageParameterResolver implements HandlerParameterResolver {

        private final Class<?> parameterType;

        public MessageParameterResolver(Class<?> parameterType) {
            this.parameterType = parameterType;
        }

        @Override
        public Object resolveParameter(Message message) {
            return message;
        }

        @Override
        public boolean support(Message message) {
            return parameterType.isInstance(message);
        }
    }
}
